package com.taobao.classloader;

import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClassLoaderRouter {
	private static Logger					logger	= LoggerFactory.getLogger(ClassLoaderRouter.class);
	private ClassLoader						defaultClassLoader;
	private List<ClassLoaderScope>			scopes;
	private Map<String, ClassLoader>		classLoaderMapping;
	private Map<String, ClassLoaderScope>	scopesMapping;

	public ClassLoaderRouter() {
		super();
		classLoaderMapping = new HashMap<String, ClassLoader>();
		scopesMapping = new HashMap<String, ClassLoaderScope>();
	}

	public void init() throws MalformedURLException {
		if (scopes != null && scopes.size() > 0) {
			for (ClassLoaderScope scope : scopes) {
				addClassLoaderScope(scope, true);
				scopesMapping.put(scope.getScopeName(), scope);
			}
		} else {
			defaultClassLoader = Thread.currentThread().getContextClassLoader();
		}
	}

	public ClassLoaderScope getClassLoaderScope(String scopeName) {
		return scopesMapping.get(scopeName);
	}

	public void addClassLoaderScope(ClassLoaderScope scope, boolean override) throws MalformedURLException {
		if (scope != null) {
			if (override || classLoaderMapping.get(scope.getScopeName().trim()) == null) {
				// URL[] urls = scope.getJarPackageURLs();
				// if (urls != null) {
				ClassLoader classLoader = new JarClassLoader(scope, Thread.currentThread().getContextClassLoader());
				classLoaderMapping.put(scope.getScopeName(), classLoader);
				if (scope.isDefaultScope()) {
					defaultClassLoader = classLoader;
				}
				// }
			}
		}
	}

	public Class<?> getObjectType(String scope, String className) throws ClassNotFoundException {
		ClassLoader classLoader = null;
		if (null != scope && !"".equals(scope)) {
			classLoader = classLoaderMapping.get(scope);
		} else {
			classLoader = getDefaultClassLoader();
		}

		return classLoader.loadClass(className);
	}

	public Class<?> getObjectType(ClassLoader classLoader, String className) throws ClassNotFoundException {
		return classLoader.loadClass(className);
	}

	private Object newInstance(ClassLoader classLoader, String className) throws ClassLoaderException {
		try {
			Class<?> myClass = getObjectType(classLoader, className);
			return myClass.newInstance();
		} catch (Throwable t) {
			throw new ClassLoaderException(t);
		}
	}

	private Object newInstance(ClassLoader classLoader, String className, Class<?>[] parasClass, Object[] paraObjects)
			throws ClassLoaderException {
		try {
			Class<?> myClass = getObjectType(classLoader, className);
			Constructor<?> con = myClass.getConstructor(parasClass);
			return con.newInstance(paraObjects);
		} catch (Throwable t) {
			throw new ClassLoaderException(t);
		}
	}

	public Object newInstance(String scope, String className) throws ClassLoaderException {
		ClassLoader classLoader = classLoaderMapping.get(scope);
		if (classLoader != null) {
			return newInstance(classLoader, className);
		} else {
			return null;
		}
	}

	public Object newInstance(String scope, String className, Class<?>[] parasClass, Object[] paraObjects) throws ClassLoaderException {
		ClassLoader classLoader = classLoaderMapping.get(scope);
		if (classLoader != null) {
			return newInstance(classLoader, className, parasClass, paraObjects);
		} else {
			return null;
		}
	}

	public ClassLoader getClassLoader(String scopeName) {
		if (null == scopeName || scopeName.equals("")) {
			return getDefaultClassLoader();
		} else {
			return classLoaderMapping.get(scopeName);
		}
	}

	private ClassLoader getDefaultClassLoader() {
		if (defaultClassLoader == null) {
			logger.debug("Default classloader is null,set current thread classloader to default classloader.");
			defaultClassLoader = Thread.currentThread().getContextClassLoader();
		}
		return defaultClassLoader;
	}

	public Object newInstance(String className) throws ClassLoaderException {
		return newInstance(getDefaultClassLoader(), className);
	}

	public Object newInstance(String className, Class<?>[] parasClass, Object[] paraObjects) throws ClassLoaderException {
		return newInstance(getDefaultClassLoader(), className, parasClass, paraObjects);
	}

	public List<ClassLoaderScope> getScopes() {
		return scopes;
	}

	public void setScopes(List<ClassLoaderScope> scopes) {
		this.scopes = scopes;
	}
}
